#include "Surface.h"
#include <algorithm>
#include <cmath>
#include <cstdio>

namespace RayTracer
{
    Surface::Surface(Vector3D pos, Vector3D dir) : Object3D(pos, dir)
    {
		this->material = new Material();
    }

    float Surface::ComputeDot(const Vector3D *surfaceNormal, const Vector3D *lightDir) const
    {
		Vector3D tmp = *lightDir;
		Vector3D *ptr = &tmp;
		Vector3D::Normalize(&tmp);
			
		return std::max(0.0f, DOTP(surfaceNormal, ptr));
    }
		
    float Surface::ComputePhongDot(const Vector3D *surfaceNormal, const Vector3D *lightDir, const Vector3D *viewOrigin, const Vector3D *hitPoint) const
    {
		Vector3D viewVec = Vector3D::Subtract(viewOrigin, hitPoint);
		Vector3D::Normalize(&viewVec);
			
		Vector3D halfVec = Vector3D::Add(lightDir, &viewVec);
		Vector3D::Normalize(&halfVec);
			
		return (float)pow(std::max(0.0f, Vector3D::Dot(&halfVec, surfaceNormal)), this->GetMaterial()->GetPhongExponent());
    }

    Color Surface::GetColor(const Light3D *light, const Vector3D *pt) const
    {
		Color tmp = this->GetMaterial()->GetColor();
		Color spec = this->GetMaterial()->GetSpecularColor();

		Vector3D n = this->Normal(pt);

		Vector3D lightPos = light->position;

		Vector3D ld = Vector3D::Subtract(&lightPos, pt);
	    			
		float dot = this->ComputeDot(&n, &ld);

		Vector3D zeroVec;
		float phongDot = this->ComputePhongDot(&n, &ld, &zeroVec, pt);

		float ri = light->GetRedInt();
		float gi = light->GetGreenInt();
		float bi = light->GetBlueInt();
				
		Color current = Color(
		tmp.r * ri * dot + spec.r * ri * phongDot,
		tmp.g * gi * dot + spec.g * gi * phongDot,
		tmp.b * bi * dot + spec.b * bi * phongDot);
	
		return current;
    }
		
    Material *Surface::GetMaterial() const
    {
		return this->material;
    }
		
    void Surface::SetMaterial(Material *value)
    {
		delete this->material;
		this->material = value;
    }

	Vector3D Surface::GetPosition() const
	{
		return this->position;
	}

    Surface::~Surface()
    {
		delete this->material;
    }
}
